﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using LitJson;
using SynoManager.Communication;
using System.IO;
using System.Collections.Specialized;

namespace SynoManager.FileStation
{
    public class FileStationClient : StationClientBase
    {
        private const int _fileStationServiceID = 5;
        private const string _fileStationServiceUrl = @"/webfm/webUI/webfm.cgi";
        private const string _treeServiceUrl = @"/webfm/webUI/file_share.cgi";
        private const string _mvcpServiceUrl = @"/webfm/webUI/file_MVCP.cgi";
        private const string _fileDeleteServiceUrl = @"/webfm/webUI/file_delete.cgi";
        private const string _extractServiceUrl = @"/webfm/webUI/file_extract.cgi";
        private const string _renameServiceUrl = @"/webfm/webUI/file_rename.cgi";

        public FileStationClient(SynoDevice device)
            : base(device, _fileStationServiceID, _fileStationServiceUrl)
        {
        }

        #region webfm.cgi
        #region getfiles
        public GetFilesResult GetFiles(string target, string need, int start, int limit, string sort, string dir)
        {
            return PostAction<GetFilesResult>("getfiles", "target={0}&need={1}&start={2}&limit={3}&sort={4}&dir={5}", target, need, start, limit, sort, dir);
        }

        public class GetFilesResult : ClientRequestResult
        {
            public GetFileResultItem[] items;
            public int total;
        }

        public class GetFileResultItem
        {
            public string file_id;
            public string filename;
            public string filesize;
            public string gid;
            public string group;
            public string icon;
            public bool is_compressed;
            public bool isdir;
            public string mod_time;
            public string owner;
            public string path;
            public int privilege;
            public string privilege_str;
            public int uid;
        }
        #endregion

        #region getfs
        public GetFSResult GetFS(string cwd)
        {
            return PostAction<GetFSResult>("getfs", "cwd={0}", cwd);
        }

        public class GetFSResult : ClientRequestResult
        {
            public string currVolume;
            public string free;
            public string used;
        }
        #endregion
        #endregion

        #region file_share.cgi
        public GetSharesResult[] GetShares()
        {
            return GetShares("fm_root");
        }

        public GetSharesResult[] GetShares(string node)
        {
            string json = PostRequest(_treeServiceUrl, "action=getshares&node=" + node);
            GetSharesResult[] array = JsonMapper.ToObject<GetSharesResult[]>(json);
            return array;
        }
        public class GetSharesResult : ClientRequestResult
        {
            public bool draggable;
            public string id;
            public bool leaf;
            public string path;
            public string qtip;
            public string right;
            public string text;
        }
#endregion
        #region file_MVCP.cgi
        public MvcpResult Move(string files, string destpath, bool overwrite)
        {
            return MoveCopy("move", files, destpath, overwrite);
        }

        public MvcpResult Copy(string files, string destpath, bool overwrite)
        {
            return MoveCopy("copy", files, destpath, overwrite);
        }

        public MvcpResult MoveCopy(string action, string files, string destpath, bool overwrite)
        {
            string json = PostRequest(_mvcpServiceUrl, "action={0}&destpath={1}&files={2}&overwrite={3}&url=webUI/file_MVCP.cgi",
                        action, destpath, files, overwrite);
            var result = JsonMapper.ToObject<MvcpResult>(json);
            return result;
        }

        public class MvcpResult : ClientRequestResult
        {
            public int pid;
            public string progress;
            public int running;
        }

        public MvcpProgressResult ReadMVCPProgress(int pid)
        {
            string json = PostRequest(_mvcpServiceUrl, "action=readMVCPprogress&pid={0}&url=webUI/file_MVCP.cgi", pid);
            var result = JsonMapper.ToObject<MvcpProgressResult>(json);
            return result;
        }

        public class MvcpProgressResult : ClientRequestResult
        {
            public string errItems;
            public string pfile;
            public int pid;
            public double progress;
            public string result;
            public int running;
        }
        #endregion

        #region file_delete.cgi
        public MvcpResult Delete(string files, string curDir)
        {
            string json = PostRequest(_fileDeleteServiceUrl, "action={0}&curDir={1}&files={2}&url=webUI/file_delete.cgi",
                                      "delete", curDir, files);
            var result = JsonMapper.ToObject<MvcpResult>(json);
            return result;
        }

        public FileDeleteProgressResult ReadDeleteProgress(int pid)
        {
            string json = PostRequest(_fileDeleteServiceUrl, "action=readdeleteprogress&pid={0}&url=webUI/file_delete.cgi", pid);
            var result = JsonMapper.ToObject<FileDeleteProgressResult>(json);
            return result;
        }

        public class FileDeleteProgressResult : ClientRequestResult
        {
            public double progress;
            public string result;
            public int running;
        }
        #endregion

        #region file_extract.cgi
        public FileEnumResult EnumFileContents(string zipfile, string curShare, int fileId, int start, int limit, string sort, string dir)
        {
            string json = PostRequest(_extractServiceUrl,
                                      "action=file_enum&curShare={0}&dir={1}&fileID={2}=limit={3}&sort={4}&start={5}&zipfile={6}",
                                      curShare, dir, fileId, limit, sort, start, zipfile);
            var result = JsonMapper.ToObject<FileEnumResult>(json);
            return result;
        }

        public MvcpResult Extract(string dest, string mode, string pass, string pathmode, string zipfile)
        {
            string json = PostRequest(_extractServiceUrl,
                                      "action=extract&dest={0}&mode={1}&pass={2}&pathmode={3}&zipfile={4}&url=webUI/file_extract.cgi");
            var result = JsonMapper.ToObject<MvcpResult>(json);
            return result;
        }

        public ExtractProgressResult ReadExtractProgress(int pid)
        {
            string json = PostRequest(_extractServiceUrl, "action=readProgress&pid={0}&url=webUI/file_extract.cgi", pid);
            var result = JsonMapper.ToObject<ExtractProgressResult>(json);
            return result;            
        }

        public class FileEnumResult : ClientRequestResult
        {
            public FileEnumResultItem[] items;
            public int total;
        }

        public class FileEnumResultItem
        {
            public int fileId;
            public bool folder;
            public string icon;
            public string mod_time;
            public string name;
            public string pack_size;
            public string path;
            public string size;
        }

        public class ExtractProgressResult : ClientRequestResult
        {
            public int running;
            public string result;
        }
        #endregion

        #region file_rename
        public ClientRequestResult Rename(string relPath, string oldname, string newname)
        {
            string json = PostRequest(_renameServiceUrl, "action=rename&fileID={0}&newname={1}&oldname={2}&url=webUI/file_rename.cgi", relPath, newname, oldname);
            var result = JsonMapper.ToObject<ExtractProgressResult>(json);
            return result;
        }
        #endregion

        #region file_crtfdr.cgi
        public ClientRequestResult CreateFolder(string relPath, string newname)
        {
            string json = PostRequest(@"/webfm/webUI/file_crtfdr.cgi", "action=createfolder&dest={0}&files={1}&url=webUI/file_crtfdr.cgi", relPath, newname);
            var result = JsonMapper.ToObject<ExtractProgressResult>(json);
            return result;
        }
        #endregion

        #region file_download.cgi
        private const string _downloadServiceUrl = @"/webfm/webUI/file_download.cgi";
        public HttpWebResponse Download(string dldir, string dlname, params string[] files)
        {
            string dlfiles = String.Join("/", files);
            string formdata = ToQueryString("dldir={0}&dlname={1}&dlfiles={2}", dldir, dlname, dlfiles);
            Uri uri = new Uri(baseUri, _downloadServiceUrl);
            return GetResponseStream(uri.ToString(), "POST", formdata);
        }
        #endregion

        #region file_upload.cgi
        private const string _uploadServiceUrl = @"/webfm/webUI/flash_upload.cgi";
        public UploadResult Upload(string destdir, string filePath, bool overwrite)
        {
            string session = cookieContainer.GetCookies(baseUri)["id"].Value;
            Uri uri = new Uri(baseUri, _uploadServiceUrl + "?" + ToQueryString("session={0}&path={1}&overwrite={2}", session, destdir, overwrite.ToString().ToLower()));
            WebClient client = new WebClient();

            // TODO: Get progress / send async
            byte[] resultBytes = client.UploadFile(uri, "POST", filePath);

            string json = Encoding.ASCII.GetString(resultBytes);
            var result = JsonMapper.ToObject<UploadResult>(json);
            return result;
        }

        public class UploadResult : ClientRequestResult
        {
            public string file;
            public int pid;
            public int progress;
            public string result;
            public string uploaddir;
        }

        //private const string _uploadServiceUrl = @"/webfm/webUI/file_upload.cgi";
        //public ClientRequestResult UploadOld(string destdir, string filePath, bool overwrite, int no, DateTime timestamp)
        //{
        //    Uri uri = new Uri(baseUri, _uploadServiceUrl);
        //    if (cookieContainer == null)
        //        Login();

        //    try
        //    {
        //        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        //        request.Method = "POST";
        //        request.Accept = "*/*";
        //        request.SendChunked = false;

        //        request.CookieContainer = cookieContainer;

        //        string boundary = "---------------------------" + 41184676334;// DateTime.Now.Ticks.ToString("x");

        //        request.ContentType = "multipart/form-data; boundary=" + boundary;
        //        request.KeepAlive = true;

        //        Stream memStream = new System.IO.MemoryStream(); 
        //        byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("\r\n--" + boundary + "\r\n"); 
        //        string formdataTemplate = "\r\n--" + boundary + "\r\nContent-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}";

        //        long unixtime = (long) timestamp.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;

        //        StringDictionary dict = new StringDictionary();
        //        dict.Add("no", no.ToString());
        //        dict.Add("overwrite", overwrite.ToString().ToLower());
        //        dict.Add("session", cookieContainer.GetCookies(uri)["id"].Value);
        //        dict.Add("timestamp", unixtime.ToString());
        //        dict.Add("path", destdir);

        //        foreach (string key in dict.Keys)
        //        {
        //            string formitem = string.Format(formdataTemplate, key, dict[key]);
        //            byte[] formitembytes = Encoding.UTF8.GetBytes(formitem);
        //            memStream.Write(formitembytes, 0, formitembytes.Length);
        //        }

        //        memStream.Write(boundarybytes, 0, boundarybytes.Length); 
        //        string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: application/octet-stream\r\n\r\n";

        //        string header = string.Format(headerTemplate, "file", Path.GetFileName(filePath)); 
        //        byte[] headerbytes = System.Text.Encoding.ASCII.GetBytes(header); 
        //        memStream.Write(headerbytes, 0, headerbytes.Length);
        //        FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); 
        //        byte[] buffer = new byte[1024]; 
        //        int bytesRead = 0; 
        //        while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
        //        {
        //            memStream.Write(buffer, 0, bytesRead);
        //        } 
        //        memStream.Write(boundarybytes, 0, boundarybytes.Length - 2);
        //        memStream.Write(Encoding.ASCII.GetBytes("--"), 0, 2);
        //        fileStream.Close();
        //        request.ContentLength = memStream.Length;
                
        //        var requestStream = request.GetRequestStream();
        //        memStream.Position = 0; 

        //        StreamReader reader = new StreamReader(memStream);
        //        string temp = reader.ReadToEnd();
        //        Debug.WriteLine(temp);
        //        memStream.Position = 0;

        //        byte[] tempBuffer = new byte[memStream.Length]; 
        //        memStream.Read(tempBuffer, 0, tempBuffer.Length); 
        //        memStream.Close(); 
        //        requestStream.Write(tempBuffer, 0, tempBuffer.Length); 
        //        requestStream.Close();

        //        var response = (HttpWebResponse)request.GetResponse();
        //        var responseStream = new StreamReader(response.GetResponseStream());

        //        // update device connected state to true (will throw an event when connection was lost)
        //        device.IsConnected = true;

        //        string json = responseStream.ReadToEnd();
        //        var result = JsonMapper.ToObject<ClientRequestResult>(json);
        //        return result;
        //    }
        //    catch (WebException ex)
        //    {
        //        device.IsConnected = false;
        //        throw new CommunicationException("Could not communicate with Station at " + device.Address, ex);
        //    }
        //}

        //public UploadProgressResult ReadUploadProgress(int no)
        //{
        //    Uri uri = new Uri(baseUri, _uploadServiceUrl);
        //    string session = cookieContainer.GetCookies(uri)["id"].Value;
        //    double timestamp = DateTime.Now.Subtract(new DateTime(1970, 1, 1).ToLocalTime()).TotalSeconds;
        //    string json = PostRequest(_extractServiceUrl, "action=readuploadprogress&lastform={0}&no={1}&session={2}&timestamp={3}&url=webUI/file_upload.cgi","true",no,session,timestamp);
        //    var result = JsonMapper.ToObject<UploadProgressResult>(json);
        //    return result;
        //}

        //public class UploadProgressResult : ClientRequestResult
        //{
        //    public string file;
        //    public double progress;
        //    public int running;
        //}
        #endregion
    }
}
